#pragma once
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/json.h>
#include <iostream>
#include <memory>
#include "logger.hpp"
namespace zjx_im{
bool Serialize(const Json::Value& val, std::string& dst)
{
    //先定义Json::streamWriter 工厂类 Json::StreamWriterBuilder
    Json::StreamWriterBuilder swb;
    swb.settings_["emitUTF8"] = true;
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    //通过Json::StreamWriter中的write接口进行序列化
    std::stringstream ss;
    int ret = sw->write(val, &ss);
    if(ret != 0)
    {
        std::cout << "Json反序列化失败!\n"; 
    }
    dst = ss.str();
    return true;
}

bool UnSerialize(const std::string &src, Json::Value& val)
{
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    std::string err;
    bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err);
    if(ret == false)
    {
        std::cout << "Json反序列化失败!" << err << std::endl;
        return false;
    }
    return true;
}
// POST /user/_doc user表示索引类型 _doc表示类型（此处是文档类型）
// { 
//     "settings" : {
//         "analysis" : { 
//             "analyzer" : { 分词器
//                 "ik" : { 中文分词器
//                     "tokenizer" : "ik_max_word"  分词粒度描述-以最大粒度进行分词
//                 } 
//             } 
//         } 
//     },
//     "mappings" : {
//         "properties" 
//         "dynamic" : true,: {
//             "昵称" : {
//                 "type" : "text",  这个字段是一个文本类型
//                 "analyzer" : "ik_max_word"  使用中文分词器
//             },
//             "用户ID" : {
//                 "type" : "keyword",  也是一个文本类型，但是是关键字，不进行分词
//                 "analyzer" : "standard"   使用默认标准分词器
//             },
//             "手机号" : {
//                 "type" : "keyword",
//                 "analyzer" : "standard" 
//             },
//             "描述" : {
//                 "type" : "text",
//                 "enabled" : false  仅做存储，不做搜索
//             },
//             "头像ID" : {
//                 "type" : "keyword"
//                 "enabled" : false  仅做存储，不做搜索
//             } 
//         } 
//     }
// }
class ESIndex
{
public:
    ESIndex(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc")
    :_name(name),
    _type(type),
    _client(client)
    {
        //初始化代码负责前半部分
        Json::Value settings;
        Json::Value analysis;
        Json::Value analyzer;
        Json::Value ik;
        Json::Value tokenizer;
        tokenizer["tokenizer"] = "ik_max_word";
        ik["ik"] = tokenizer;
        analyzer["analyzer"] = ik;
        analysis["analysis"] = analyzer;
        _index["settings"] = analysis;

        

        std::string str;
        Serialize(settings, str);
        std::cout << str << std::endl;
    }
    //"昵称" : {
    //    "type" : "text",  这个字段是一个文本类型
    //    "analyzer" : "ik_max_word"  使用中文分词器
    //},
    ESIndex& append(const std::string &key, const std::string &type = "text", const std::string &analyzer = "ik_max_word", bool enabled = true)
    {
        Json::Value fields;
        fields["type"] = type;
        fields["analyzer"] = analyzer;
        if(enabled == false) fields["enabled"] = enabled;
        _properties[key] = fields;
        return *this; //直接返回对象本身，可以直接连续创建
    }
    bool create(const std::string &index_id = "default_index_id") 
    {
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properties"] = _properties;
        _index["mappings"] = mappings;
        
        std::string body;
        bool ret = Serialize(_index, body);
        if(ret == false)
        {
            LOG_ERROR("索引序列化失败！");
            return false;
        }
        // LOG_INFO("请求正文：[{}]", body);
        //2.发起搜索请求
        try
        {
            auto rsp = _client->index(_name, _type, index_id, body); //转义
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                LOG_ERROR("创建ES索引{}失败 响应状态码异常：{}", _name, rsp.status_code);
                return false;
            }
        }catch(std::exception &e)
        {
            std::cout << "请求失败：" << e.what() << std::endl;
            LOG_ERROR("创建ES索引{}失败 失败：{}", _name, e.what());
            return false;
        }
        LOG_DEBUG("测试索引创建成功！");
        return true;
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _properties;
    Json::Value _index;
    std::shared_ptr<elasticlient::Client> _client;
};

class ESInsert
{
public:
    ESInsert(std::shared_ptr<elasticlient::Client>& client,
    const std::string &name,
    const std::string &type = "_doc")
    :_name(name)
    ,_type(type)
    ,_client(client)
    {}
    template<typename T>
    ESInsert &append(const std::string& key, const T &val)
    {
        _item[key] = val;
        return *this;
    }
    bool insert(const std::string id = "")
    {
        std::string body;
        bool ret = Serialize(_item, body);
        if(ret == false)
        {
            LOG_ERROR("索引序列化失败！");
            return false;
        }
        //2.发起搜索请求
        try
        {
            auto rsp = _client->index(_name, _type, id, body); //转义
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                LOG_ERROR("新增数据{}失败 响应状态码异常：{}", body, rsp.status_code);
                return false;
            }
        }catch(std::exception &e)
        {
            std::cout << "请求失败：" << e.what() << std::endl;
            LOG_ERROR("新增数据{}失败 失败：{}", body, e.what());
            return false;
        }
        return true;
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _item;
    std::shared_ptr<elasticlient::Client> _client;
};

class ESRemove
{
public:
    ESRemove(std::shared_ptr<elasticlient::Client> &client,
    const std::string &name,
    const std::string &type = "_doc"):
    _name(name), _type(type), _client(client){}

    bool remove(const std::string &id)
    {
        try
        {
            auto rsp = _client->remove(_name, _type, id); //转义
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                LOG_ERROR("删除数据{}失败 响应状态码异常：{}", _name, rsp.status_code);
                return false;
            }
        }catch(std::exception &e)
        {
            std::cout << "请求失败：" << e.what() << std::endl;
            LOG_ERROR("删除数据{}失败 失败：{}", _name, e.what());
            return false;
        }
        return true;
    }
private:
    std::string _name;
    std::string _type;
    std::shared_ptr<elasticlient::Client> _client;

};

class ESSearch
{
public:
    ESSearch(std::shared_ptr<elasticlient::Client> &client,
        const std::string &name,
        const std::string &type = "_doc"):
        _name(name), _type(type), _client(client){}
    //追加一个必须不遵循的条件
    ESSearch& append_must_not_terms(const std::string& key, const std::vector<std::string> &vals) //必须不遵循的条件
    {
        Json::Value fields;
        for(const auto& val : vals)
        {
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        _must_not.append(terms);
        return *this;
    }
    ESSearch& append_should_match(const std::string& key, const std::string &val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _should.append(match);
        return *this;
    }
    ESSearch &append_must_term(const std::string& key, const std::string &val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value term;
        term["term"] = field;
        _must.append(term);
        return *this;
    }
    ESSearch &append_must_match(const std::string& key, const std::string &val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _must.append(match);
        return *this;
    }
    Json::Value search()
    {
        Json::Value cond;
        if(_must_not.empty() == false) cond["must_not"] = _must_not;
        if(_should.empty() == false) cond["should"] = _should;
        if(_must.empty() == false) cond["must"] = _must;
        Json::Value query;
        query["bool"] = cond;
        Json::Value root;
        root["query"] = query;
        std::string body;
        bool ret = Serialize(root, body);
        if(ret == false)
        {
            LOG_ERROR("索引序列化失败！");
            return Json::Value();;
        }
        //LOG_DEBUG("检索正文: {}", body)
        //2.发起搜索请求
        cpr::Response rsp;
        try
        {
            rsp = _client->search(_name, _type, body); //转义
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                LOG_ERROR("检索数据{}失败 响应状态码异常：{}", body, rsp.status_code);
                return Json::Value();
            }

        }catch(std::exception &e)
        {
            std::cout << "请求失败：" << e.what() << std::endl;
            LOG_ERROR("检索数据{}失败 失败：{}", body, e.what());
            return Json::Value();
        }
        //3.需要对相应正文进行反序列化为Json::Value对象
        LOG_DEBUG("检索响应正文{}", rsp.text);
        Json::Value json_res;
        ret = UnSerialize(rsp.text, json_res);
        if(ret == false)
        {
            LOG_ERROR("检索数据{}反序列化失败", rsp.text);
            return Json::Value();
        }
        return json_res["hits"]["hits"];
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _must_not;
    Json::Value _should;
    Json::Value _must;
    std::shared_ptr<elasticlient::Client> _client;
};
}